package main;

import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import java.util.Hashtable;

import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler;

import main.constraint.XDF;

import automata.Fsa;
import automata.Label;
import automata.State;

@SuppressWarnings("unchecked")

public class KeysVal extends DefaultHandler
{
	//private String xml;
	private String chVal;

	private Configuration conf1;
	private ConfigurationSet confSet1;
	private ConfigurationSet confSetAux;

	private Vector<ConfigurationSet> confVec;
	private Vector<SyntAttributes> syntVec;

	private KeysExecution ke = new KeysExecution();

	private Stack<Vector<ConfigurationSet>> stackConf;
	private Stack<Vector<SyntAttributes>> stackSynt;

	private StringBuffer posSt;
	private int posRight;
	
	public Vector htVec;
	public Vector<Hashtable<String,ArrayList<Inters>>> interVec;
	public Hashtable<String, ArrayList<String>> htErrors;
	
	public KeysVal()
	{
		//xml = new String();
	}

    /**
	 * Feature for Web
	 */		
	public String parseKeysValWeb(InputStream uri, KeysExecution kexec)
	{
		InputSource source = new InputSource(uri);		
		return parseKeysValFunction(source, kexec);
	}
	
    /**
	 * Feature for GUI
	 */	
	public String parseKeysVal(String uri, KeysExecution kexec)
	{
	    InputSource source = new InputSource(uri);	    
		return parseKeysValFunction(source, kexec);	    
	}
	
    /**
	 * Main function, makes validating, by scanning the document
	 * @param uri path of the xml document
	 * @param kexec set of constraint
	 * @return vector format string to print on the screen
	 */			
	public String parseKeysValFunction(InputSource source, KeysExecution kexec)
	{
		SAXParser sp = new SAXParser();

        //Initial.ErrorMessage = new String();
		confSetAux = new ConfigurationSet();
		confVec = new Vector<ConfigurationSet>();
		syntVec = new Vector<SyntAttributes>();

		stackConf = new Stack<Vector<ConfigurationSet>>();//this is a new stack with vector of configurationSet
		stackSynt = new Stack<Vector<SyntAttributes>>();//this is a new stack with vector of SynntAttributes

		htVec = new Vector();
		interVec = new Vector<Hashtable<String,ArrayList<Inters>>>();
		
		htErrors = new Hashtable<String, ArrayList<String>>();//create for all constraint  one hashtable of errors
		
		ke = kexec;
     	posSt = new StringBuffer("r");
		posRight = 0;
			
	    //InputSource source = source;
		sp.setContentHandler(this);

		// For syntAtt we need three lists: Context, left and right paths
		//SyntAttributes syntAtt = new SyntAttributes();

		// List of the constraints being verified
		ArrayList<XDF> hsKeys = ke.getKeysSet();//XDF contains all that we check
		
		/*
		 * Here the initial attribute Conf is defined for each constraint in KeySet:
		 * Conf k1={M.e0}
		 * Each constraint has a set of attributes Conf, defined for 
		 * each node element.
		 * The attribute Conf is added to the set of Configurations (ConfSet).
		 * The ConfSet is added (for each constraint) in a vector. 
		 */		
		for (Iterator<XDF> i = hsKeys.iterator(); i.hasNext();)
		{
			//Root.conf={M.q1 | (q0, label) -> q1}
			XDF k = (XDF) i.next();
			Fsa f = k.getContextFsa();
			State s = f.get_start_state();
			SyntAttributes syntAtt = new SyntAttributes();
			conf1 = new Configuration(s, f, k); //Conf k = f.s
			confSet1 = new ConfigurationSet();
			confSet1.add(conf1);
			confVec.add(confSet1);
			syntVec.add(syntAtt);			
			htErrors.put(k.getNameConstraint(), new ArrayList<String>());
			
			//The hashtables are used to store the values found for each constraint (xdf)
			Hashtable htContextXfd = new Hashtable();
			htVec.add(htContextXfd);
			
			Hashtable<String,ArrayList<Inters>> htIntersXfd = new Hashtable<String,ArrayList<Inters>>();
			interVec.add(htIntersXfd);		
		}
		
		System.out.println("Vetor de HTs:"+htVec.toString());		
		System.out.println("Vetor de Intersecoes:"+interVec.toString());
		// Stacks CONF and SYNT are used to store inherited and synthesized attributes respectively.
		stackConf.push(confVec);
		stackSynt.push(syntVec);

		try
		{
			sp.parse(source);
			return (stackSynt.peek().toString());			
		}		
		catch (Exception e)
		{
			System.out.println("Error Parse Validation");
			e.printStackTrace();
		}
		
		return (htVec.toString());		
	}

	
	// Teste de Performance
	public String parseKeysValTestes(String uri, KeysExecution kexec)
	{
		SAXParser sp = new SAXParser();

        //Initial.ErrorMessage = new String();
		confSetAux = new ConfigurationSet();
		confVec = new Vector<ConfigurationSet>();//and an array of configurationSet that has several XDF
		syntVec = new Vector<SyntAttributes>();

		stackConf = new Stack<Vector<ConfigurationSet>>();//this is a new stack with vector of configurationSet
		stackSynt = new Stack<Vector<SyntAttributes>>();//this is a new stack with vector of SynntAttributes

		htVec = new Vector<Hashtable>();
		interVec = new Vector<Hashtable<String,ArrayList<Inters>>>();
		
		ke = kexec;
     

		posSt = new StringBuffer("r");
		posRight = 0;
	
		
	    InputSource source = new InputSource(uri);
		sp.setContentHandler(this);

		// For syntAtt we need three lists: Context, left and right paths
		//SyntAttributes syntAtt = new SyntAttributes();

		// List of the constraints being verified
		ArrayList<XDF> hsKeys = ke.getKeysSet();//XDF contains all that we check
		
		/*
		 * Here the initial attribute Conf is defined for each constraint in KeySet:
		 * Conf k1={M.e0}
		 * Each constraint has a set of attributes Conf, defined for 
		 * each node element.
		 * The attribute Conf is added to the set of Configurations (ConfSet).
		 * The ConfSet is added (for each constraint) in a vector. 
		 */		
		for (Iterator<XDF> i = hsKeys.iterator(); i.hasNext();)
		{
			//Root.conf={M.q1 | (q0, label) -> q1}
			XDF k = (XDF) i.next();
			Fsa f = k.getContextFsa();
			State s = f.get_start_state();
			SyntAttributes syntAtt = new SyntAttributes();
			conf1 = new Configuration(s, f, k); //Conf k = f.s
			confSet1 = new ConfigurationSet();
			confSet1.add(conf1);
			confVec.add(confSet1);
			syntVec.add(syntAtt);
			
			//The hashtables are used to store the values found for each constraint (xdf)
			Hashtable htContextXfd = new Hashtable();
			htVec.add(htContextXfd);
			
			Hashtable<String,ArrayList<Inters>> htIntersXfd = new Hashtable<String,ArrayList<Inters>>();
			interVec.add(htIntersXfd);		
		}
		
		//System.out.println("Vetor de HTs:"+htVec.toString());		
		//System.out.println("Vetor de Intersecoes:"+interVec.toString());
		// Stacks CONF and SYNT are used to store inherited and synthesized attributes respectively.
		stackConf.push(confVec);
		stackSynt.push(syntVec);

		try
		{
			// execution time - begin
			long media = 0;
			int i;

//		    System.out.println("Teste TesteWeb, arquivo L4.xml (com 230201 elementos). (sem mensagens sendo exibidas no console durante a execu��o).");
//			System.out.println("( com a xdf: (db, ({/projet/pname} -> /projet [N])) )");

			File f = new File ("testeWebTempo.txt");
			f.createNewFile(); // cria o arquivo fisicamente
			FileWriter fileWriter = new FileWriter (f,false); // true para n�o apagar o que tinha no arquivo, cc false
			PrintWriter printWriter = new PrintWriter(fileWriter);
			int j = 0;			
			InputSource source1 = new InputSource("arquivosXFD\\L4.xml");
			ArrayList <InputSource> arquivos =  new ArrayList <InputSource>();
			InputSource source2 = new InputSource("arquivosXFD\\L1.xml");
			InputSource source3 = new InputSource("arquivosXFD\\L6.xml");
			InputSource source4 = new InputSource("arquivosXFD\\L5.xml");
			
			ArrayList <String> nomes = new ArrayList<String>();
			nomes.add("arquivo L4 ");
			nomes.add("arquivo L6 ");
			nomes.add("arquivo L5 ");
			nomes.add("arquivo L1 ");
			
			ArrayList <String> tamanhos = new ArrayList<String>();
			tamanhos.add("230201");
			tamanhos.add("304101");
			tamanhos.add("451601");
			tamanhos.add("4516010");
			
			arquivos.add(source1);
			arquivos.add(source3);
			arquivos.add(source4);	
			arquivos.add(source2);
			
			printWriter.println("************************** TesteWeb tempos **************************\n");				
			System.out.println("************************** TesteWeb tempos **************************\n");				
			for (InputSource source21: arquivos)
			{
				printWriter.println("\nTeste TesteWeb, arquivo: " + nomes.get(j)+ " (sem mensagens sendo exibidas no console durante a execucao).\nTamanho do arquivo: "+tamanhos.get(j) +" (em tags)\n");				
				System.out.println("\nTeste TesteWeb, arquivo: " + nomes.get(j)+ " (sem mensagens sendo exibidas no console durante a execucao).\nTamanho do arquivo: "+tamanhos.get(j) +" (em tags)\n");				
				j++;
				for (i = 1; i <= 10; i++)
				{
					long tempoInicial = System.currentTimeMillis();
					//sp.parse(source);//this function call the SAXParser and checks startElement and endElement of doc XML
					sp.parse(source21);
					long tempoFinal = System.currentTimeMillis();
					media = media + (tempoFinal - tempoInicial);

					System.out.println("\nTempo da "+ i + "-esima execucao: " + (tempoFinal - tempoInicial)/1000.0 +" segundos, ou " +(tempoFinal - tempoInicial)+ " milissegundos.");
					printWriter.println("\nTempo da "+ i + "-esima execucao: " + (tempoFinal - tempoInicial)/1000.0 +" segundos, ou " +(tempoFinal - tempoInicial)+ " milissegundos.");


				}	
				media  = media/10;
				System.out.println("Media de 10" + " execucoes: "+ media/1000.0 + " segundos, ou " +media+ " milissegundos.\n");		
				printWriter.println("Media de 10" + " execucoes: "+ media/1000.0 + " segundos, ou " +media+ " milissegundos.\n");
				System.out.println("*************************************************************************************************");
				printWriter.println("*************************************************************************************************");

			}
			printWriter.flush();
			printWriter.close();				
			//System.out.println("Vetor de HTs:"+htVec.toString());
			//System.out.println("Vetor de Intersecoes:"+interVec.toString());
		
			return (stackSynt.peek().toString());			
		}
		
		catch (Exception e)
		{
			System.out.println("Error Parse Validation");
			e.printStackTrace();
		}
		
		return (htVec.toString());
	}
  
	
	// Fim - teste de performance 
	
	
	
	public void startDocument()
	{
		//xml = "<?xml version =\"1.0\"?>\n";
	}

	public void endDocument()
	{
		//System.out.println("elements + attributes: " + counter);
	}

	/**
	 * This function read the start element of tag from xml
	 * This function is implemented of SAXParser
	 * @param String nameSpaceURI
	 * @param String localNome
	 * @param String rawName its name of tag XML
	 * @oaram Atributes atters
	 * @return void  
	 */
	public void startElement(String namespaceURI, String localName,
							 String rawName, Attributes attrs)
	{			
		chVal = new String();
		confSetAux = new ConfigurationSet();
		Label l = new Label(rawName);
		Vector<SyntAttributes> vecSynt = new Vector<SyntAttributes>();
		ArrayList data = new ArrayList();

		// List of the constraints being verified
		ArrayList<XDF> keyList = ke.getKeysSet();
		for (Iterator<XDF> i = keyList.iterator(); i.hasNext();)
		{
			i.next();
			SyntAttributes syntAtt = new SyntAttributes();
	        syntAtt.setNodeName(rawName);
	        //syntAtt.addC(true);
			vecSynt.add(syntAtt);
		}

		posSt.append(".");
		posSt.append(posRight);
		posRight = 0;
		
		//counter++;
		//posSt1.append(counter);
		
		//System.out.println("AQUI"+posSt);		
		System.out.println("Abertura -> "+rawName+" pos:"+posSt+" vecSynt: "+vecSynt.toString());
        //System.out.println(posSt);

		//System.out.println(counter);
				
		confVec = (Vector<ConfigurationSet>) stackConf.peek();//remove vector of ConfigurationSet of stackConf
		Vector<ConfigurationSet> vecAux = new Vector<ConfigurationSet>();//this a new vector auxiliary
		
		for (Iterator<ConfigurationSet> i = confVec.iterator(); i.hasNext();)//for all constraints do:
		{
			confSet1 = (ConfigurationSet) i.next();
			confSetAux = confSet1.changeConfiguration(l);
			
			vecAux.add(confSetAux);
		}
		
		stackConf.push(vecAux);//insert vect auxiliary in stackConf
		stackSynt.push(vecSynt);

		//attributes are already leaves, so they are treated here
		int len = attrs.getLength();
		if (len > 0)//A->data
		{
			data.clear();
			for (int i = 0; i < len; i++)
			{			
				posSt.append(".");
				posSt.append(posRight);
				posRight++;
				String sa = attrs.getQName(i);
				String sb = (String) attrs.getValue(i);
				
				int j = posSt.lastIndexOf(".");
				
				Label la = new Label(sa);
				vecSynt = (Vector<SyntAttributes>) stackSynt.pop();
				confVec = (Vector<ConfigurationSet>) stackConf.peek();
				
				for (int k = 0; k < confVec.size(); k++)//for all keys do:
				{
					Hashtable<String,ArrayList<Inters>> htIntersXfd = interVec.elementAt(k);
					confSet1 = (ConfigurationSet) confVec.elementAt(k);
					confSetAux = confSet1.changeConfiguration(la); //alteracao
					confSetAux.keyValidationData(vecSynt, k, sb, posSt.substring(0,j), posSt.toString(), rawName,htIntersXfd);
				}
				
				stackSynt.push(vecSynt);
				posSt.delete(j,posSt.length());
			}
		}
	}

	/**
	 * This function clean the characteres
	 * @return void
	 */
	public void characters(char ch[], int start, int length)
	{
		String str = new String(ch, start, length);
		str = str.trim();
		chVal += str; 
		//System.out.println(chVal);
	}

	/**
	 * This function read the end element of tag from xml
	 * This function is implemented of SAXParser
	 * @param String nameSpaceURI
	 * @param String localNome
	 * @param String rawName its name of tag XML
	 * @return void  
	 */	
	public void endElement(String namespaceURI, String localName, String rawName)
	{
		/* First part: to treat the chVal (that contains the data value between
		 * open and close tags
		 * Data values are enclosed in the respective structures, for all constraints
		 * and are stored in the stacks
		 */
		System.out.println("CLOSING Node name:"+rawName);
		Vector<SyntAttributes> vecSyntAnt;
		
		ArrayList<XDF> al = (ArrayList<XDF>) ke.getKeysSet();
		Vector<SyntAttributes> vecSynt;

	    int j = posSt.lastIndexOf(".");
		posRight = Integer.valueOf(posSt.substring(j+1)).intValue();		
		posRight++;		
		
		if(!chVal.isEmpty())//A->data
		{		
			confVec = (Vector<ConfigurationSet>) stackConf.pop();//lastElement();
			vecSynt = (Vector<SyntAttributes>) stackSynt.pop();
			vecSyntAnt = (Vector<SyntAttributes>) stackSynt.pop();
			
			for (int k = 0; k < confVec.size(); k++)//for all keys do:
			{
				confSet1 = (ConfigurationSet) confVec.elementAt(k);
				Hashtable<String,ArrayList<Inters>> htIntersXfd = interVec.elementAt(k);
				vecSyntAnt = confSet1.keyValidationData(vecSyntAnt, k, chVal, posSt.substring(0,j), posSt.toString(), rawName, htIntersXfd);
				//System.out.println("Val Data ("+k+"): "+vecSyntAnt);
			}
			
			stackSynt.push(vecSyntAnt);
		}		
		else//A->a1,...,an
		{
			// Extraction of values that are at the bottom of the stacks
			confVec = (Vector<ConfigurationSet>) stackConf.pop();
			
			int k;

			vecSynt = (Vector<SyntAttributes>) stackSynt.pop();	
			vecSyntAnt = (Vector<SyntAttributes>) stackSynt.pop();
			
			for (k = 0; k < confVec.size(); k++)// for each constraint : appropriate validation
			{
				confSet1 = (ConfigurationSet) confVec.elementAt(k);
				Hashtable<ArrayList<XDF>, String> htContextXfd = (Hashtable<ArrayList<XDF>, String>) htVec.elementAt(k);
				Hashtable<String,ArrayList<Inters>> htIntersXfd = interVec.elementAt(k);				
				SyntAttributes stH = (SyntAttributes)vecSynt.elementAt(k);
				
				System.err.println(stH.toString());
				
		    	vecSyntAnt = confSet1.keyValidationInNode(vecSyntAnt, k, stH, posSt.substring(0,j), posSt.toString(), rawName, htContextXfd, htIntersXfd, htErrors);		    
			}

			System.out.println("Vetor HT:" +htVec.toString());
			System.out.println("Vetor :" +vecSyntAnt.toString());

			stackSynt.push(vecSyntAnt);				
		}
		
		
		vecSynt=null;
		posSt.delete(j,posSt.length());
		chVal = "";//new String();
	}

	
	public String getResult()
	{
		return htVec.toString();
	}

	public KeysExecution getKeysExecution()
	{
		return ke;
	}

	public void setKeysExecution(KeysExecution keArg)
	{
		ke = keArg;
	}
	
	public String getErrosConstraint()
	{
		Enumeration<String> values = htErrors.keys();
		String value;
		String str = "";
		while(values.hasMoreElements())
		{ 
			value = (String) values.nextElement(); 
			for(String item : htErrors.get(value))
				str += "[" + value + "]: " + item + "\n"; 
		} 			
		
		return str;
	}
	
	public String printConfigurationSet(ConfigurationSet cs)
	{
		String confFsa = new String("{");
		
		for (Iterator<Configuration> i = cs.iterator(); i.hasNext();)
		{
			Configuration c = (Configuration) i.next();
			confFsa = confFsa + c.getFsa().toString();
			confFsa = confFsa + c.getState().toString();
			confFsa = confFsa + ",";
		}
		
		confFsa = confFsa + "}";
		//System.out.println(confFsa);
		
		return confFsa;
	}

}